home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 025a / gsdb25.zip / GS_DBASE.PAS < prev    next >
Pascal/Delphi Source File  |  1991-08-01  |  51KB  |  1,083 lines

  1. {-----------------------------------------------------------------------------
  2.                            dBase III File Handler
  3.  
  4.        GS_DBASE Copyright (c)  Richard F. Griffin
  5.  
  6.        15 November 1990
  7.  
  8.        102 Molded Stone Pl
  9.        Warner Robins, GA  31088
  10.  
  11.        -------------------------------------------------------------
  12.        This unit handles the objects for all dBase III file (.DBF)
  13.        operations.
  14.  
  15.                    SHAREWARE  -- COMMERCIAL USE RESTRICTED
  16.  
  17.  
  18.  
  19.        Changes:
  20.  
  21.        16 Nov 90 - Moved Pack method to GS_dBFld.
  22.  
  23.        02 May 91 - Added an IndexSignature constant to the index units so the
  24.                    GS_dBase unit can confirm the index unit in use.  The flag
  25.                    IsDB3NDX is true if the dBase III index unit is used.  This
  26.                    is needed to properly convert date fields for an index.  The
  27.                    dBase III index requires a julian date instead of the
  28.                    character field stored in the record.  Most other indexes
  29.                    use the field as stored (YYYYMMDD).
  30.  
  31.        03 May 91 - Added routine to convert a date field to julian date when
  32.                    used as an index field in PutRec.
  33.  
  34.        06 Jun 91 - Fixed error in Open that caused the status not to be set
  35.                    to 'NotUpdated'.  Comment close bracket was missing, and
  36.                    caused the next instruction to be ignorred.
  37.  
  38.                    Added a UnInit method to release buffer memory from the
  39.                    Heap when the file is no longer needed.  If the file is
  40.                    to be used again, it must be reinitialized by calling
  41.                    the Init method.  This allows several files to use the
  42.                    same object, one after the other.  For example:
  43.  
  44.                            obj.Init('FILE1');
  45.                            obj.Open;
  46.                            obj.GetRec(.....);
  47.                            obj.Close;
  48.                            obj.UnInit;
  49.                            obj.Init('FILE2');
  50.                            obj.Open;
  51.                            obj.GetRec(.....);
  52.                            obj.Close;
  53.                            obj.UnInit;
  54.  
  55.  
  56. ------------------------------------------------------------------------------}
  57. {
  58.                            ┌──────────────────────┐
  59.                            │  INTERFACE SECTION:  │
  60.                            └──────────────────────┘
  61. }
  62.  
  63. unit GS_DBASE;
  64.  
  65. interface
  66.  
  67. uses
  68.      CRT,
  69.      DOS,
  70.      GS_KeyI,
  71.      GS_Date,
  72.      GS_FileH,    {File handler}
  73.      GS_Strng,    {String handling Routines}
  74.      GS_Error,    {Error Handling routines}
  75.      GS_DBNdx;    {Unit for index operations (.NDX files)}
  76.  
  77. const
  78.    GS_dBase_MaxRecBytes = 4000;      {dBASE III record limit }
  79.    GS_dBase_MaxRecField = 128;       {dBASE III field limit}
  80.    GS_dBase_MaxMemoRec  = 512;       {Size of each block of memo file data}
  81.  
  82.    Next_Record = -1;   {Token value passed to read next record}
  83.    Prev_Record = -2;   {Token value passed to read previous record}
  84.    Top_Record  = -3;   {Token value passed to read first record}
  85.    Bttm_Record = -4;   {Token value passed to read final record}
  86.  
  87.    GS_dBase_UnDltChr = 32;   {Character for Undeleted Record}
  88.    GS_dBase_DltChr   = 42;   {Character for Deleted Record}
  89.  
  90. type
  91.  
  92.    GS_dBase_Status = (NotOpen, NotUpdated, Updated);
  93.            {Flags to indicate status of dBase III file }
  94.  
  95.    GS_dBase_dRec = ^GS_dBase_DataRecord;
  96.            {Pointer type used in object descriptions to locate the memory}
  97.            {array in bytes for the dBase record.  Uses GS_dBase_DataRecord}
  98.            {defined below.}
  99.  
  100.    GS_dBase_DataRecord = ARRAY[0..GS_dBase_MaxRecBytes] OF Byte;
  101.            {Defines an array of bytes in memory that is as large as the }
  102.            {maximum size of a dBase record (GS_dBase_MaxRecBytes).}
  103.  
  104. {
  105.         ┌──────────────────────────────────────────────────────────────────┐
  106.         │  ********         Data Structure Description         **********  │
  107.         │                                                                  │
  108.         │  The following record defines the dBase III file header.  Refer  │
  109.         │  to Appendix A for an explanation of each data element.          │
  110.         └──────────────────────────────────────────────────────────────────┘
  111. }
  112.    GS_dBase_Head = Record
  113.                       DBType     : Byte;
  114.                       Year       : Byte;
  115.                       Month      : Byte;
  116.                       Day        : Byte;
  117.                       RecCount   : LongInt;
  118.                       Location   : Integer;
  119.                       RecordLen  : Integer;
  120.                       Reserved   : Array[1..20] of Byte;
  121.                    end;
  122.  
  123. {
  124.      ┌──────────────────────────────────────────────────────────────────┐
  125.      │  *********             Field Descriptor              *********   │
  126.      │                                                                  │
  127.      │  This record defines the field descriptor.  There is one of      │
  128.      │  these for each field defined in the database structure.  They   │
  129.      │  are stacked as 32 bytes following the file header record, as    │
  130.      │  described in Appendix A.                                        │
  131.      └──────────────────────────────────────────────────────────────────┘
  132. }
  133.  
  134.    GS_dBase_Field = Record
  135.                        FieldName    : String[10];
  136.                                       {Array[1..11] of Char actually}
  137.                                       {This is to simplify conversion}
  138.                        FieldType    : Char;
  139.                        FieldAddress : LongInt;
  140.                        FieldLen     : Byte;
  141.                        FieldDec     : Byte;
  142.                        Reserved     : Array[1..14] of Char;
  143.                     end;
  144.  
  145.    GS_dBase_dFld = ^GS_dBase_DataField;
  146.           {Pointer type used in object descriptions to assign memory}
  147.           {for storing the field descriptors.                          }
  148.  
  149.    GS_dBase_DataField = ARRAY[1..GS_dBase_MaxRecField] OF GS_dBase_Field;
  150.           {Defines an array of field descriptors (GS_dBase_Field) that}
  151.           {is as large as the maximum number of dBase fields allowed}
  152.           {(GS_dBase_MaxRecFields).}
  153.  
  154.    GS_dBase_nFld = ^GS_dBase_NameField;
  155.           {Pointer type used in object descriptions to assign memory}
  156.           {for storing the field name strings.                      }
  157.  
  158.    GS_dBase_NameField = Array[1..GS_dBase_MaxRecField] OF string[11];
  159.           {Defines an array of field name strings (GS_dBase_Field) that}
  160.           {is as large as the maximum number of dBase fields allowed}
  161.           {(GS_dBase_MaxRecFields).}
  162.  
  163.  
  164. {
  165.        ┌──────────────────────────────────────────────────────────────┐
  166.        │  ***********      dBase Object Definition      ************  │
  167.        └──────────────────────────────────────────────────────────────┘
  168. }
  169.  
  170.    GS_dBase_DB = object(GS_KeyI_Objt) {Make it a child for keyboard control}
  171.       FileName     : string[64];      {Stores FileName of dBase File}
  172.       dFile        : file;            {File Type to reference data file}
  173.       mFile        : file;            {File Type to reference memo file}
  174.       HeadProlog   : GS_dBase_Head;   {Image of file header}
  175.       dStatus      : GS_dBase_Status; {Holds Status Code of file}
  176.       WithMemo     : Boolean;         {True if memo file present}
  177.       DateOfUpdate : string[8];       {MM/DD/YY of last update}
  178.       NumRecs      : LongInt;         {Number of records in file}
  179.       HeadLen      : Integer;         {Header + Field Descriptor length}
  180.       RecLen       : Integer;         {Length of record}
  181.       NumFields    : Integer;         {Number of fields in the record}
  182.       Fields       : GS_dBase_dFld;   {Pointer to memory array holding}
  183.                                       {field descriptors}
  184.       FieldsN      : GS_dBase_nFld;   {Pointer to memory array holding}
  185.                                       {Field name strings}
  186.       RecNumber    : LongInt;         {Physical record number last read}
  187.       CurRecord    : GS_dBase_dRec;   {Pointer to memory array holding}
  188.                                       {the current record data.  Refer}
  189.                                       {to Appendix B for record structure}
  190.       DelFlag      : boolean;         {True if record deleted}
  191.       File_EOF     : boolean;         {True if at end of file }
  192.       Found        : boolean;         {Set True on valid record Find}
  193.       dbfNdxTbl    : array [1..16] of GS_Indx_LPtr;
  194.                                       {Holds addresses of up to 16 Index}
  195.                                       {Objects.  The first array is the}
  196.                                       {Master Index.  For File changes,}
  197.                                       {this array will be used to ensure}
  198.                                       {all indexes are updated. }
  199.       dbfNdxActv   : boolean;         {True if an index file is used}
  200.  
  201. {
  202.    ┌───────────────────────────────────────────────────────────────────────┐
  203.    │  ***  These methods are described individually in the following  ***  │
  204.    │        pages.  As seen here, their name describes their function      │
  205.    └───────────────────────────────────────────────────────────────────────┘
  206. }
  207.  
  208.       PROCEDURE Append;
  209.       PROCEDURE Blank;
  210.       PROCEDURE Close;
  211.       FUNCTION  Create(FName : string) : boolean;
  212.       PROCEDURE Delete;
  213.       FUNCTION  Find(st : string) : boolean;
  214.       FUNCTION  Formula(st : string; var ftyp : char) : string; virtual;
  215.       PROCEDURE GetRec(RecNum: LongInt);
  216.       PROCEDURE Index(IName : String);
  217.       PROCEDURE Index_List(RecAct: LongInt; var I_List; var RNum : longint);
  218.       CONSTRUCTOR Init(FName : string);
  219.       PROCEDURE Open;
  220.       PROCEDURE PutRec(RecNum : LongInt);
  221.       PROCEDURE UnDelete;
  222.       PROCEDURE UnInit;
  223.    end;
  224.  
  225. var
  226.    IsDB3NDX : boolean;
  227. {
  228.                          ┌──────────────────────────┐
  229.                          │  IMPLEMENTATION SECTION  │
  230.                          └──────────────────────────┘
  231. }
  232.  
  233. implementation
  234. uses
  235.    GS_dB3Wk;                          {Use shown here to avoid circular def.}
  236.  
  237.  
  238. CONST
  239.   DB3File = 3;                        {First byte of dBase III(+) file}
  240.   DB3WithMemo = $83;                  {First byte of dBase III(+) file}
  241.                                       {if memo file (.DBT) is present }
  242.  
  243.  
  244. PROCEDURE GS_dBase_DB.Append;
  245. BEGIN
  246.    PutRec(0);
  247.         {Calls objectname.PutRec method with a record number of}
  248.         {zero.  This causes the record number to default to }
  249.         {objectname.NumRecs + 1.                              }
  250. END;
  251.  
  252.  
  253. PROCEDURE GS_dBase_DB.Blank;
  254. begin
  255.    FillChar(CurRecord^[0], RecLen, ' ');
  256.                                       {Fill spaces for RecLen bytes}
  257. end;
  258.  
  259.  
  260. PROCEDURE GS_dBase_DB.Close;
  261. CONST
  262.    EofMark : Byte = $1A;              {ASCII code for EOF byte}
  263. var
  264.    rsl,
  265.    yy, mm, dd, wd : word;             {Local variables to get today's}
  266.                                       {date through TP's GetDate procedure}
  267.    i              : integer;          {work variable}
  268. {
  269.        ┌──────────────────────────────────────────────────────────────┐
  270.        │   The Update_File procedure is called if any records are     │
  271.        │   added/updated while the file is open.  This is indicated   │
  272.        │   by objectname.dStatus set to 'UpDated'.  The procedure     │
  273.        │   inserts the current date in the file header, updates the   │
  274.        │   record count, rewrites the file header, and writes an EOF  │
  275.        │   byte at the end of the file.                               │
  276.        └──────────────────────────────────────────────────────────────┘
  277. }
  278.    procedure UpDate_File;
  279.    BEGIN
  280.       GetDate (yy,mm,dd,wd);          {Call TP's GetDate procedure}
  281.       HeadProlog.year := yy-1900;     {Extract the Year}
  282.       HeadProlog.month := mm;         {Extract the Month}
  283.       HeadProlog.day := dd;           {Extract the Day}
  284.       HeadProlog.RecCount := NumRecs; {Update number records in file}
  285.       GS_FileWrite(dFile, 0, HeadProlog, 8, rsl);
  286.       GS_FileWrite(dFile, HeadLen+NumRecs*RecLen, EofMark, 1, rsl); {EOF marker}
  287.    END;   { IF Updated }
  288.  
  289. {
  290.          ┌───────────────────────────────────────────────────────────┐
  291.          │  Beginning of CLOSE Procedure.                            │
  292.          │      1.  Exit if file not open                            │
  293.          │      2.  Update the file header if records added/updated  │
  294.          │      3.  Close the file                                   │
  295.          │      4.  Close the .DBT memo file if applicable           │
  296.          │      5.  Set objectname.dStatus to 'NotOpen'              │
  297.          └───────────────────────────────────────────────────────────┘
  298. }
  299.  
  300. begin
  301.    IF dStatus = NotOpen THEN exit;    {Exit if file not open}
  302.    IF dStatus = Updated THEN UpDate_File;
  303.                                       {Write new header information if the}
  304.                                       {file was updated in any way}
  305.    GS_FileClose(dFile);
  306.    if WithMemo then GS_FileClose(mFile);
  307. {
  308.          ┌──────────────────────────────────────────────────────────┐
  309.          │  The following routine releases index files associated   │
  310.          │  with the .DBF file and releases memory.                 │
  311.          └──────────────────────────────────────────────────────────┘
  312. }
  313.    i := 1;                         {initialize counter}
  314.    while dbfNdxTbl[i] <> nil do
  315.    begin
  316.       dbfNdxTbl[i]^.Ndx_Close;     {Close this index file}
  317.       dispose(dbfNdxTbl[i]);       {Release Heap Memory}
  318.       dbfNdxTbl[i] := nil;         {set pointer to 'empty'}
  319.       inc(i);                      {increment counter}
  320.    end;
  321.    dbfNdxActv := false;
  322.    dStatus := NotOpen;             {Set objectname.dStatus to 'NotOpen'}
  323. END;                        { GS_dBase_Close }
  324.  
  325.  
  326. Function GS_dBase_DB.Create(FName : string) : boolean;
  327. begin
  328.    if GS_dB3_Create(FName) then Create := true else Create := false;
  329. END;                        { GS_dBase_Create }
  330.  
  331.  
  332. PROCEDURE GS_dBase_DB.Delete;
  333. begin
  334.    DelFlag := true;                   {Set Delete Flag to true}
  335.    CurRecord^[0] := GS_dBase_DltChr;  {Put '*' in first byte of current record}
  336.    PutRec(RecNumber);                 {Write the current record to disk }
  337. end;                 {GS_dBase_Delete}
  338.  
  339.  
  340. {
  341.                                    FIND
  342.  
  343.  
  344.      ╔══════════════════════════════════════════════════════════════════╗
  345.      ║                                                                  ║
  346.      ║   The FIND method will search the master index file for the      ║
  347.      ║   key string contained in the calling argument.                  ║
  348.      ║                                                                  ║
  349.      ║   Note:  At this time, numeric fields must have a string value   ║
  350.      ║          argument, and date fields are not handled.              ║
  351.      ║                                                                  ║
  352.      ║       Calling the Method:                                        ║
  353.      ║                                                                  ║
  354.      ║           objectname.Find(String)                                ║
  355.      ║                                                                  ║
  356.      ║               ( where objectname is of type GS_dBase_DB,         ║
  357.      ║                       String is key value to match)              ║
  358.      ║                                                                  ║
  359.      ║       Result:                                                    ║
  360.      ║                                                                  ║
  361.      ║           Matching record is read if found.  No error check,     ║
  362.      ║           but index object Found flag is set true on match.      ║
  363.      ║                                                                  ║
  364.      ╚══════════════════════════════════════════════════════════════════╝
  365. }
  366.  
  367. Function GS_dBase_DB.Find(st : string) : boolean;
  368. var
  369.    RNum   : longint;
  370. begin
  371. {
  372.          ┌───────────────────────────────────────────────────────────┐
  373.          │  The next statement checks to see if an index is active   │
  374.          │  (dbfNdxActv = true), and calls the index object's        │
  375.          │  KeyFind method if true.  The key string is passed to     │
  376.          │  the method as the only argument.  The matching record    │
  377.          │  is returned from the method.  If there is no match,      │
  378.          │  the method returns a zero value.  Note that the method   │
  379.          │  is called using the first index object pointer in array  │
  380.          │  dbfNdxTabl (the master index).  The ability to use an    │
  381.          │  object pointer in place of an actual object is a highly  │
  382.          │  useful tool.                                             │
  383.          └───────────────────────────────────────────────────────────┘
  384. }
  385.    if (dbfNdxActv) then
  386.    begin
  387.       RNum := dbfNdxTbl[1]^.KeyFind(st);
  388.       if RNum > 0 then                {RNum = 0 if no match, otherwise}
  389.                                       {it holds the valid record number}
  390.       begin
  391.          GetRec(RNum);                {If match found, read the record}
  392.          Found := True;               {Set Match Found flag true}
  393.       end else
  394.       begin                           {If no matching index key, then}
  395.          Found := False;              {Set Match Found Flag False}
  396.       end;
  397.    end else                           {If there is no index file, then}
  398.       Found := False;                 {Set Match Found Flag False}
  399.    Find := Found;
  400. end;                  {GS_dBase_Find}
  401.  
  402.  
  403. function GS_dBase_DB.Formula(st : string; var ftyp : char) : string;
  404. begin
  405.    ShowError(399,'Object for field handling missing');
  406.    Formula := '';
  407. end;
  408.  
  409.  
  410. {
  411.                                    GETREC
  412.  
  413.  
  414.    ╔═══════════════════════════════════════════════════════════════════════╗
  415.    ║                                                                       ║
  416.    ║  The GETREC method will access the dBase III file to retrieve the     ║
  417.    ║  record number passed in the call.                                    ║
  418.    ║                                                                       ║
  419.    ║      Calling the Method:                                              ║
  420.    ║                                                                       ║
  421.    ║            objectname.GetRec (RecNum)                                 ║
  422.    ║                                                                       ║
  423.    ║                   ( where objectname is of type GS_dBase_DB,          ║
  424.    ║                           RecNum is the record number to retrieve.    ║
  425.    ║                           **  If a number greater than 0, record      ║
  426.    ║                               will be physical number from .DBF;      ║
  427.    ║                               if Next_Record, Prev_Record,            ║
  428.    ║                               Top_Record, or Bttm_Record, then        ║
  429.    ║                               the appropriate record will be found.   ║
  430.    ║                               For these codes, if an index is in      ║
  431.    ║                               use, the record will be retrieved       ║
  432.    ║                               based on it's location in the index.)   ║
  433.    ║                                                                       ║
  434.    ║       Result:                                                         ║
  435.    ║                                                                       ║
  436.    ║            1.  Record is retrieved based on record number argument    ║
  437.    ║            2.  Objectname.RecNumber set to record number read         ║
  438.    ║            3.  Objectname.DelFlag set true if deleted record          ║
  439.    ║            4.  If last record of file (.DBF or .NDX), then            ║
  440.    ║                objectname.File_EOF set true.                          ║
  441.    ║                                                                       ║
  442.    ╚═══════════════════════════════════════════════════════════════════════╝
  443. }
  444.  
  445.  
  446. PROCEDURE GS_dBase_DB.GetRec(RecNum : LongInt);
  447. VAR
  448.    dFilea : FileRec absolute dFile;
  449.    i,
  450.    Result : Integer;                  {Local working variable}
  451.    RNum   : LongInt;                  {Local working variable  }
  452.    StrFil : String[80];
  453.    rsl    : word;
  454. BEGIN
  455.    if NumRecs = 0 then
  456.    begin
  457.       File_EOF := true;
  458.       exit;
  459.    end;
  460.    RNum := RecNum;                    {Store RecNum locally for modification}
  461.    File_EOF := false;                 {Initialize End of File Flag to false}
  462.  
  463. {
  464.          ┌───────────────────────────────────────────────────────────┐
  465.          │  The next statement checks to see if an index is active   │
  466.          │  (dbfNdxActv = true), and calls the index object's        │
  467.          │  KeyRead method if true and the record requested is       │
  468.          │  a relative record (less than 0).  Note that the method   │
  469.          │  is called using the first index object pointer in array  │
  470.          │  dbfNdxTabl (the master index).  The ability to use an    │
  471.          │  object pointer in place of an actual object is a highly  │
  472.          │  useful tool.  Upon return, the index file's EOF flag is  │
  473.          │  stored as the .DBF's End-of-File Flag.                   │
  474.          └───────────────────────────────────────────────────────────┘
  475. }
  476.    if (dbfNdxActv) and (RecNum < 0) then
  477.    begin
  478.       RNum := dbfNdxTbl[1]^.KeyRead(RecNum);
  479.                                       {Get record number of physical}
  480.                                       {record to read from .DBF.}
  481.       File_EOF :=dbfNdxTbl[1]^.KeyEOF;
  482.                                       {Get index EOF flag.  The EOF will be}
  483.                                       {set when a KeyRead of Next_Record}
  484.                                       {will go past the last index record}
  485.    end
  486.    else
  487.       if (dbfNdxActv) and (RNum > 0) and  (RNum <= NumRecs) then
  488.          if not dbfNdxTbl[1]^.KeyLocRec(RecNum) then exit;
  489.                                       {If physical record search, set index}
  490.                                       {to the same record.}
  491.    if File_EOF then exit;             {Return if EOF reached}
  492. {
  493.          ┌──────────────────────────────────────────────────────────┐
  494.          │  The value in RNum is tested to see if it is a relative  │
  495.          │  record seek or a physical record number.  The number    │
  496.          │  is also tested to ensure it is in the file record       │
  497.          │  range of valid numbers.  Note, if an index was read,    │
  498.          │  RNum will now be a physical record.                     │
  499.          └──────────────────────────────────────────────────────────┘
  500. }
  501.    case RNum of
  502.       Next_Record : begin
  503.                        RNum := RecNumber + 1;
  504.                                       {Get next sequential record}
  505.                        if RNum > NumRecs then
  506.                        begin          {If beyond number of records in file,}
  507.                                       {you must recover}
  508.                           RNum := NumRecs;
  509.                                       {Reset to final record}
  510.                           File_EOF := true;
  511.                                       {Set EOF Flag to True}
  512.                           exit;       {Return from GetRec}
  513.                        end;
  514.                     end;
  515.       Prev_Record : begin
  516.                        RNum := RecNumber - 1;
  517.                                       {Get Previous Record}
  518.                        if RNum < 1 then RNum := 1;
  519.                                       {If at beginning of file, stay}
  520.                     end;
  521.       Top_Record  : RNum := 1;        {Set to the first record}
  522.       Bttm_Record : RNum := NumRecs;  {Set to the last record}
  523.    end;
  524.    if (RNum < 1) or (RNum > NumRecs) then
  525.    begin                              {if a physical record number is out}
  526.                                       {of range, exit with error}
  527.       i := 0;
  528.       Str(RNum, StrFil);
  529.       StrFil := 'Record ' + StrFil;
  530.       StrFil := StrFil + ' Out of Range for File ';
  531.       while dFilea.Name[i] <> #0 do
  532.       begin
  533.          StrFil := StrFil + dFilea.Name[i];
  534.          inc(i);
  535.       end;
  536.       ShowError(100,StrFil);
  537.       exit;                           {Terminate read attempt if record number}
  538.                                       {is out of range}
  539.    end;
  540.    GS_FileRead(dFile, HeadLen+(RNum-1) * RecLen, CurRecord^, RecLen, rsl);
  541.                                       {Read RecLen bytes into memory buffer}
  542.                                       {for the correct physical record}
  543.    RecNumber := RNum;                 {Set objectname.RecNumber = this record }
  544.    if CurRecord^[0] = GS_dBase_DltChr then DelFlag := true
  545.          else DelFlag := false;       {Set objectname.DelFlag to show status}
  546.                                       {of the record's Delete byte}
  547. END;                  {GetRec}
  548.  
  549.  
  550. {
  551.                                    INDEX
  552.  
  553.  
  554.      ╔══════════════════════════════════════════════════════════════════╗
  555.      ║                                                                  ║
  556.      ║   The INDEX method initializes the index array in objectname     ║
  557.      ║   and assigns the first index as the master index.  The other    ║
  558.      ║   index files will be updated upon .DBF updates (when the        ║
  559.      ║   index write entries are added).                                ║
  560.      ║                                                                  ║
  561.      ║       Calling the Method:                                        ║
  562.      ║                                                                  ║
  563.      ║           objectname.Index(String)                               ║
  564.      ║                                                                  ║
  565.      ║               ( where objectname is of type GS_dBase_DB,         ║
  566.      ║                       String is list of index files, separated   ║
  567.      ║                       by spaces.                                 ║
  568.      ║                                                                  ║
  569.      ║       Result:                                                    ║
  570.      ║                                                                  ║
  571.      ║           Index files are assigned and the master index is       ║
  572.      ║           opened.                                                ║
  573.      ║                                                                  ║
  574.      ╚══════════════════════════════════════════════════════════════════╝
  575. }
  576.  
  577.  
  578. Procedure GS_dBase_DB.Index (IName : String);
  579. var
  580.    i,j : integer;                     {Local working variable  }
  581.    st  : String[64];                  {Local working variable}
  582. begin
  583. {
  584.              ┌───────────────────────────────────────────────────┐
  585.              │  Reset index file array.                          │
  586.              │     1.  Close open index files                    │
  587.              │     2.  Release index objects stored on the heap  │
  588.              │     3.  Set array pointers to nil.                │
  589.              └───────────────────────────────────────────────────┘
  590. }
  591.    i := 1;
  592.    while dbfNdxTbl[i] <> nil do
  593.    begin
  594.       dbfNdxTbl[i]^.Ndx_Close;
  595.       Dispose(dbfNdxTbl[i]);
  596.       dbfNdxTbl[i] := nil;
  597.       inc(i);
  598.    end;
  599.    dbfNdxActv := false;               {Set index active flag to false}
  600. {
  601.            ┌──────────────────────────────────────────────────────┐
  602.            │  This routine scans the input string for the names   │
  603.            │  of index files.  Names must be separated by commas  │
  604.            │  or spaces.  The .NDX extension must not be part     │
  605.            │  of the file name                                    │
  606.            └──────────────────────────────────────────────────────┘
  607. }
  608.    i := 0;                            {i will hold count of index files}
  609.    j := 1;
  610.    st := '';
  611.    while j <= length(IName) do
  612.    begin
  613. {
  614.                ┌───────────────────────────────────────────────┐
  615.                │  Build an index file name in st until end of  │
  616.                │  input string, a comma, or a space is found   │
  617.                └───────────────────────────────────────────────┘
  618. }
  619.       if (IName[j] <> ' ') and (IName[j] <> ',') then
  620.          st := st + IName[j]
  621.       else
  622.       begin                           {When file string is complete:}
  623.          inc(i);                      {Increment index file count}
  624.          if st <> '' then             {   If not an empty string:  }
  625.          begin
  626.             New(dbfNdxTbl[i]);        {Get heap memory for index object}
  627.             if dbfNdxTbl[i]^.Init(st) then
  628.             begin                     {Initialize index object}
  629.             end;
  630.          end;
  631.          st := '';                    {Reset file name to empty for next}
  632.       end;
  633.       inc(j);                         {Inc counter for next input string char }
  634.    end;
  635. {
  636.               ┌─────────────────────────────────────────────────┐
  637.               │  This routine is needed to finish out when the  │
  638.               │  input string is finished.  Note the routine    │
  639.               │  above does not create an index entry at the    │
  640.               │  end of the input string.  That is done here.   │
  641.               └─────────────────────────────────────────────────┘
  642. }
  643.    if st <> '' then
  644.    begin
  645.       inc(i);
  646.       New(dbfNdxTbl[i]);
  647.       if dbfNdxTbl[i]^.Init(st) then
  648.       begin
  649.       end;
  650.    end;
  651.    if i > 0 then dbfNdxActv := true;  {Set index active flag true if index }
  652.                                       {files are found  }
  653. end;
  654.  
  655. {
  656.                                  INDEX_LIST
  657.  
  658.  
  659.      ╔══════════════════════════════════════════════════════════════════╗
  660.      ║                                                                  ║
  661.      ║   The INDEX_LIST method returns the index key field from the     ║
  662.      ║   index used as the master index.  This is done instead of the   ║
  663.      ║   normal action of reading the .DBF file.  Only the index file   ║
  664.      ║   is read during this method.  A common use of this method is    ║
  665.      ║   to build a memory table of keys and associated record numbers. ║
  666.      ║                                                                  ║
  667.      ║       Calling the Method:                                        ║
  668.      ║                                                                  ║
  669.      ║           objectname.Index_LIST(RecNum, String, RNum)            ║
  670.      ║                                                                  ║
  671.      ║               ( where objectname is of type GS_dBase_DB,         ║
  672.      ║                       RecAct is the index key to retrieve.       ║
  673.      ║                          (Top_Record, Next_Record,               ║
  674.      ║                           Prev_Record, or Bttm_Record)           ║
  675.      ║                                                                  ║
  676.      ║                       String is field to place key value.        ║
  677.      ║                       RNum is field to place record number.      ║
  678.      ║                                                                  ║
  679.      ║       Result:                                                    ║
  680.      ║                                                                  ║
  681.      ║           The master Index file is accessed based on RecAct.     ║
  682.      ║           The value in the key field entry is returned in        ║
  683.      ║           String.  The record's location id the .DBF file is     ║
  684.      ║           returned in RecNum.  File_EOF is set upon an attempt   ║
  685.      ║           to access beyond the last index entry.                 ║
  686.      ║                                                                  ║
  687.      ╚══════════════════════════════════════════════════════════════════╝
  688. }
  689.  
  690.  
  691. Procedure GS_dBase_DB.Index_List(RecAct: LongInt; var I_List;
  692.                                  var RNum : longint);
  693. var
  694.    I_L : string[255] absolute I_List;
  695.                                       {Redefines I_List for internal use}
  696. BEGIN
  697. {
  698.          ┌───────────────────────────────────────────────────────────┐
  699.          │  The next statement checks to see if an index is active   │
  700.          │  (dbfNdxActv = true), and calls the index object's        │
  701.          │  KeyRead method if true and the record requested is       │
  702.          │  a relative record (less than 0).  Note that the method   │
  703.          │  is called using the first index object pointer in array  │
  704.          │  dbfNdxTabl (the master index).                           │
  705.          └───────────────────────────────────────────────────────────┘
  706. }
  707.    if (dbfNdxActv) and (RecAct < 0) then
  708.    begin
  709.       RNum := dbfNdxTbl[1]^.KeyRead(RecAct);
  710.       if RNum > 0 then                {if good read, RNum will be > 0}
  711.       begin
  712.          I_L := dbfNdxTbl[1]^.Ndx_Key_St;
  713.                                       {get key value, and store in the}
  714.                                       {I_List variable, using I_L which}
  715.                                       {points to the same memory location}
  716.       end else
  717.       begin
  718.          RNum := 0;                   {set null value if no valid read}
  719.          I_L := '';                   {set null value if no valid read}
  720.       end;
  721.       File_EOF := dbfNdxTbl[1]^.KeyEOF;
  722.                                       {move index EOF flag to File_EOF};
  723.    end;
  724. end;
  725.  
  726. {
  727.                                 INIT
  728.  
  729.  
  730.      ╔══════════════════════════════════════════════════════════════════╗
  731.      ║                                                                  ║
  732.      ║   The INIT method initializes objectname by reading the .DBF     ║
  733.      ║   file and loading file structure information into the object.   ║
  734.      ║   It also checks for a memo file (.DBT) and assigns that to      ║
  735.      ║   a file type if it exists.  This routine must be called         ║
  736.      ║   before using the other methods in objectname.                  ║
  737.      ║                                                                  ║
  738.      ║       Calling the Method:                                        ║
  739.      ║                                                                  ║
  740.      ║           objectname.Init(String)                                ║
  741.      ║                                                                  ║
  742.      ║               ( where objectname is of type GS_dBase_DB,         ║
  743.      ║                       String is the file name of the dBase       ║
  744.      ║                       file (without the .DBF extension).         ║
  745.      ║                                                                  ║
  746.      ║       Result:                                                    ║
  747.      ║                                                                  ║
  748.      ║           DBase file object is initialized and memo file is      ║
  749.      ║           initialized.                                           ║
  750.      ║                                                                  ║
  751.      ╚══════════════════════════════════════════════════════════════════╝
  752. }
  753.  
  754. CONSTRUCTOR GS_dBase_DB.Init(FName : string);
  755. var
  756.    i : integer;                       {Local working variable}
  757.  
  758. {
  759.            ┌───────────────────────────────────────────────────────┐
  760.            │  The ProcessHeader Procedure stores information from  │
  761.            │  the dBase III .DBF file into objectname.             │
  762.            └───────────────────────────────────────────────────────┘
  763. }
  764.  
  765.    PROCEDURE ProcessHeader;
  766.    VAR
  767.       dFilea : FileRec absolute dFile;
  768.       StrFil : string[80];
  769.       WSt    : string[12];
  770.       Result : word;
  771.       ofs    : longint;
  772.       o, i   : Integer;               {Local working variables}
  773.       m,dy,y : string[2];             {Local working variables}
  774.    BEGIN             {ProcessHeader}
  775. {
  776.               ┌─────────────────────────────────────────────────┐
  777.               │  Test to ensure file is a dBase III .DBF file.  │
  778.               │  Exit with error if it is not.  Set the         │
  779.               │  objectname.WithMemo flag if memo file present. │
  780.               └─────────────────────────────────────────────────┘
  781. }
  782.       CASE HeadProlog.DBType OF
  783.          DB3File : WithMemo := False;
  784.          DB3WithMemo : WithMemo := True;
  785.          ELSE
  786.          BEGIN
  787.             GS_FileClose(dFile);      {If not a valid dBase file, close}
  788.             StrFil := '';
  789.             i := 0;
  790.             while dFilea.Name[i] <> #0 do
  791.             begin
  792.                StrFil := StrFil + dFilea.Name[i];
  793.                inc(i);
  794.             end;
  795.             StrFil := StrFil + ' not a dBase III file';
  796.             ShowError(157,StrFil);
  797.             Exit;
  798.          END;
  799.       END;                      {CASE}
  800. {
  801.                 ┌─────────────────────────────────────────────┐
  802.                 │  Convert numeric date fields to ASCII text  │
  803.                 └─────────────────────────────────────────────┘
  804. }
  805.       Str(HeadProlog.month,m);
  806.       if length(m) = 1 then m := '0'+m;
  807.       Str(HeadProlog.day,dy);
  808.       if length(dy) = 1 then dy := '0'+dy;
  809.       Str(HeadProlog.year,y);
  810.       if length(y) = 1 then y := '0'+y;
  811.       DateOfUpdate := m + '/' + dy + '/' + y;
  812.  
  813.       NumRecs := HeadProlog.RecCount; {Number of records in file}
  814.       HeadLen := HeadProlog.Location; {Starting byte location of first record}
  815.       RecLen := HeadProlog.RecordLen; {Length of each record}
  816.       RecNumber := 0;                 {Set current record to zero}
  817.       File_EOF := false;              {Set End of File flag to false}
  818.  
  819.       GetMem(Fields, HeadLen-33);     {Allocate memory for fields buffer.}
  820.                                       {Compute total header size as length of}
  821.                                       {header file information (32 bytes),}
  822.                                       {End of Header mark (1 byte), and the}
  823.                                       {field descriptors (32 bytes each).}
  824.                                       {Size - 33 = memory required by fields}
  825.  
  826.       NumFields := (HeadLen - 33) div 32;
  827.                                       {Each field descriptor is 32 bytes}
  828.                                       {Field descriptor area of header can}
  829.                                       {be divided by 32 to get field count}
  830.  
  831.       GS_FileRead(dFile, -1, Fields^, HeadLen-33, Result);
  832.                                       {Read field descriptor portion of header}
  833.  
  834.       GetMem(FieldsN, NumFields*12);  {Allocate memory for fields buffer.}
  835.  
  836.       ofs := 1;                       {Find offset for each field}
  837.       for i := 1 to NumFields do
  838.       begin
  839.          Fields^[i].FieldAddress := ofs;
  840.          ofs := ofs + Fields^[i].FieldLen;
  841.          move(Fields^[i].FieldName,WSt[1],11);
  842.          WSt[0] := #11;
  843.          WSt[0] := char(pred(pos(#0,WSt)));
  844.          WSt := TrimR(WSt);        {Remove trailing spaces}
  845.          FieldsN^[i] := WSt;
  846.       end;
  847.    END;                      {ProcessHeader}
  848.  
  849. {
  850.          ┌──────────────────────────────────────────────────────────┐
  851.          │  The GetHeader Procedure does the initial file read.     │
  852.          │  Reads the first 32 bytes of .DBF file.  This contains   │
  853.          │  information on record size, field descriptor size,      │
  854.          │  last date updated.  Starting point for all other        │
  855.          │  file structure information.                             │
  856.          └──────────────────────────────────────────────────────────┘
  857. }
  858.  
  859.    PROCEDURE GetHeader;
  860.    VAR
  861.       Result : Word;
  862.    BEGIN                { GetHeader }
  863.       GS_FileRead(dFile, 0, HeadProlog, 32, Result);
  864.       ProcessHeader;
  865.    END;                 { GetHeader }
  866.  
  867. {
  868.               ┌─────────────────────────────────────────────────┐
  869.               │  Beginning of INIT Procedure.  It does the      │
  870.               │  following:                                     │
  871.               │      1.  Assigns .DBF extension to the file.    │
  872.               │      2.  Opens the file.                        │
  873.               │      3.  Gets header information for the        │
  874.               │          objectname object.                     │
  875.               │      4.  Closes file.                           │
  876.               │      5.  Allocates memory for a record buffer   │
  877.               │      6.  Sets file status to 'Not Open'.        │
  878.               │      7.  Sets Index Active to false.            │
  879.               │      8.  If memo file, assigns a file type.     │
  880.               └─────────────────────────────────────────────────┘
  881. }
  882.  
  883. begin
  884.    Filename := FName+'.DBF';          {Assign .DBF file extension}
  885.    GS_FileAssign(dFile, FileName,8192);
  886.    GS_FileReset(dFile, 1);
  887.    GetHeader;                         {Load file structure information into}
  888.                                       {objectname}
  889.    GS_FileClose(dFile);               {Finished with file for now}
  890.    GetMem(CurRecord, RecLen);      {Allocate memory for record buffer}
  891.    dStatus := NotOpen;                {Set file status to 'Not Open'   }
  892.    dbfNdxActv := false;               {Set index active flag to false}
  893.    for i := 1 to 16 do dbfNdxTbl[i] := nil;
  894.                                       {Set index object pointer array to nil}
  895.    if WithMemo then
  896.    begin
  897.       GS_FileAssign(mFile, FName+'.DBT',2048);
  898.                                       {If a memo file is attached, then assign}
  899.                                       {it to a file type.  This must be done}
  900.                                       {here so all future objects can get to}
  901.                                       {the file if necessary.}
  902.    end;
  903.    GS_KeyI_Objt.Init;                 {Initialize parent object}
  904. end;
  905.  
  906.  
  907.  
  908. {
  909.                                      OPEN
  910.  
  911.  
  912.      ╔══════════════════════════════════════════════════════════════════╗
  913.      ║                                                                  ║
  914.      ║   The OPEN method checks to see if the file referenced by        ║
  915.      ║   objectname is already open.  If it is open, no other action    ║
  916.      ║   is taken.  If the file is not open, then it and its memo       ║
  917.      ║   file, if one exists, is opened and flags are set.              ║
  918.      ║                                                                  ║
  919.      ║       Calling the Method:                                        ║
  920.      ║                                                                  ║
  921.      ║           objectname.Open                                        ║
  922.      ║                                                                  ║
  923.      ║               ( where objectname is of type GS_dBase_DB )        ║
  924.      ║                                                                  ║
  925.      ║       Result:                                                    ║
  926.      ║                                                                  ║
  927.      ║           1.  If file already opened, no action is taken.        ║
  928.      ║                                                                  ║
  929.      ║           otherwise:                                             ║
  930.      ║                                                                  ║
  931.      ║           1.  .DBF file is opened.                               ║
  932.      ║           2.  File status set to 'Not Updated'.                  ║
  933.      ║           3.  If memo file exists, .DBT file is opened.          ║
  934.      ║           4.  Current record number is set to zero.              ║
  935.      ║                                                                  ║
  936.      ╚══════════════════════════════════════════════════════════════════╝
  937. }
  938.  
  939.  
  940. PROCEDURE GS_dBase_DB.Open;
  941. BEGIN              { GS_dBase_Open }
  942.    if dStatus = NotOpen then          {Do only if file not already open}
  943.    begin
  944.       GS_FileAssign(dFile, FileName,4096);
  945.       GS_FileReset(dFile, 1);         {Open .DBF file}
  946.       dStatus := NotUpdated;          {Set status to 'Not Updated' }
  947.       if WithMemo then GS_FileReset(mFile,GS_dBase_MaxMemoRec);
  948.                                       {If memo file, then open .DBT file}
  949.       RecNumber := 0;                 {Set current record to zero }
  950.       Blank;                          {Clear the record buffer}
  951.    end;
  952. END;               { GS_dBase_Open }
  953.  
  954. {
  955.                                  PUTREC
  956.  
  957.  
  958.      ╔══════════════════════════════════════════════════════════════════╗
  959.      ║                                                                  ║
  960.      ║   The PUTREC method will write an updated record to the dBase    ║
  961.      ║   III(+) .DBF file.  The data to be written must be stored       ║
  962.      ║   in objectname.CurRecord^ prior to calling the method.          ║
  963.      ║                                                                  ║
  964.      ║       Calling the Method:                                        ║
  965.      ║                                                                  ║
  966.      ║           objectname.PutRec(RecNum)                              ║
  967.      ║                                                                  ║
  968.      ║               ( where objectname is of type GS_dBase_DB,         ║
  969.      ║                       RecNum is physical record number to        ║
  970.      ║                       write to.  If not within the range of      ║
  971.      ║                       existing records, it record will be        ║
  972.      ║                       appended to the end of the file.           ║
  973.      ║                                                                  ║
  974.      ║       Result:                                                    ║
  975.      ║                                                                  ║
  976.      ║           1.  If RecNum not in range of existing records         ║
  977.      ║               it will be appended and objectname.NumRecs         ║
  978.      ║               incremented by one.                                ║
  979.      ║           2.  Record will be written.                            ║
  980.      ║           3.  RecNum will become current record number.          ║
  981.      ║           4.  File status will be changed to 'Updated'.          ║
  982.      ║                                                                  ║
  983.      ╚══════════════════════════════════════════════════════════════════╝
  984. }
  985.  
  986.  
  987. PROCEDURE GS_dBase_DB.PutRec(RecNum : LongInt);
  988. VAR
  989.    Result : Word;                     {Local Variable}
  990.    RNum   : LongInt;                  {Local Variable}
  991.    IKey   : String;                   {Local Variable for Key Formula string}
  992.    ftyp   : Char;
  993.    fval   : LongInt;
  994. BEGIN
  995.    RNum := RecNum;                    {Move RecNum to local variable for }
  996.                                       {possible modification}
  997. {
  998.                 ┌─────────────────────────────────────────────┐
  999.                 │  If Record Number not in range of existing  │
  1000.                 │  records, append it to the end of file.     │
  1001.                 └─────────────────────────────────────────────┘
  1002. }
  1003.    IF (RNum > NumRecs) or (RNum < 1) then
  1004.    begin
  1005.       inc(NumRecs);                   {Increment record count}
  1006.       RNum := NumRecs;                {Put last record number in RNum}
  1007.    end;
  1008.    GS_FileWrite(dFile, HeadLen+(RNum-1)*RecLen, CurRecord^, RecLen, Result);
  1009.    RecNumber := RNum;              {Store record number as current record }
  1010.    dStatus := Updated;             {Set file status to 'Updated'}
  1011. {
  1012.          ┌───────────────────────────────────────────────────────────┐
  1013.          │  The next statement checks to see if an index is active   │
  1014.          │  (dbfNdxActv = true), and calls the index object's        │
  1015.          │  KeyUpdate method if true.   Note that the method         │
  1016.          │  is called using the first index object pointer in array  │
  1017.          │  dbfNdxTabl (the master index).                           │
  1018.          └───────────────────────────────────────────────────────────┘
  1019. }
  1020.    if (dbfNdxActv) then
  1021.    begin
  1022.       IKey := Formula(dbfNdxTbl[1]^.Ndx_Key_Form,ftyp);
  1023.       if (IsDB3NDX) and (ftyp = 'D') then
  1024.       begin
  1025.          fval := GS_Date_Juln(IKey);
  1026.          str(fval,IKey);
  1027.       end;
  1028.       dbfNdxTbl[1]^.KeyUpdate(IKey,RNum,RecNum);
  1029.    end;
  1030. END;                        {PutRec}
  1031. {.pa}
  1032. {
  1033.                                   UNDELETE
  1034.  
  1035.  
  1036.    ╔═══════════════════════════════════════════════════════════════════════╗
  1037.    ║                                                                       ║
  1038.    ║  The UNDELETE method will reset the Delete flag in the dBase III(+)   ║
  1039.    ║  file.                                                                ║
  1040.    ║                                                                       ║
  1041.    ║      Calling the Method:                                              ║
  1042.    ║                                                                       ║
  1043.    ║            objectname.UnDelete                                        ║
  1044.    ║                                                                       ║
  1045.    ║                   ( where objectname is of type GS_dBase_DB)          ║
  1046.    ║                                                                       ║
  1047.    ║       Result:                                                         ║
  1048.    ║                                                                       ║
  1049.    ║            1.  objectname.DelFlag is set false.                       ║
  1050.    ║            2.  A ' ' (UnDelete flag) is set in byte 0 of current      ║
  1051.    ║                file.                                                  ║
  1052.    ║            3.  PutRec is called to write current record to disk.      ║
  1053.    ║                                                                       ║
  1054.    ╚═══════════════════════════════════════════════════════════════════════╝
  1055. }
  1056.  
  1057.  
  1058. PROCEDURE GS_dBase_DB.UnDelete;
  1059. begin
  1060.    DelFlag := false;                  {Set Delete flag to false}
  1061.    CurRecord^[0] := GS_dBase_UnDltChr;
  1062.                                       {Put ' ' in first byte of current record}
  1063.    PutRec(RecNumber);                 {Write the current record to disk }
  1064. end;
  1065.  
  1066. {                         Free buffer memory}
  1067.  
  1068. PROCEDURE GS_dBase_DB.UnInit;
  1069. begin
  1070.    Close;
  1071.    FreeMem(FieldsN, NumFields*12);  {DeAllocate memory for fields list.}
  1072.    FreeMem(CurRecord, RecLen);      {DeAllocate memory for record buffer}
  1073.    FreeMem(Fields, HeadLen-33);     {DAllocate memory for fields buffer.}
  1074. end;
  1075.  
  1076.  
  1077.  
  1078. begin
  1079.    if IndexSignature = 'NDX3' then IsDB3NDX := true else IsDB3NDX := false;
  1080. end.
  1081.  
  1082.  
  1083.